using System;
using System.Collections.Generic;
using System.Linq;
using OS.Infrastructure.Domain;
using OS.Infrastructure.NHibernate;
using OS.Infrastructure.NHibernate.Repositories;
using TaskKernel.Domain.Interface;

namespace TaskKernel.Domain.Nhibernate.NHibernateLayer
{
    public class QueryTask : BaseRepository<Task>, IQueryTask
    {
        public QueryTask(ISessionProvider sessionProvider, ILinqProvider linqProvider)
            : base(sessionProvider, linqProvider) {}

        #region IQueryTask Members

        public virtual Task GetTask(Guid id)
        {
            return FindBy(x => x.UID == id);
        }

        //public virtual Task GetTaskByCode(int code)
        //{
        //    return FindBy(x => x.Code == code);
        //}

        public virtual IQueryable<Task> GetArchiveTasksInOwnDepartment(Account account, Department department)
        {
            return FilterBy(x => x.Status == TaskStatus.Closed).
                Where(x => x.Department.UID == department.UID).OrderByDescending(x => x.DateClose);
        }

        public IEnumerable<Task> RequestCloseTasksWithTimeCreate(DateTime dateTime)
        {
            return
                FilterBy(new WhereTaskStatusIs(TaskStatus.WaitingToClose)).AsEnumerable().Where(
                    new WhereTasksDateCreate(dateTime).IsSatisfiedBy());
        }

        public IEnumerable<Task> WaitToCloseTasksOwnerInDepartment(Account account, Department department)
        {
            var spec = new WhereTaskStatusIs(TaskStatus.WaitingToClose) & new AccountIsOwner(account) &
                       new InDepartment(department);
            return GetTasksBySpecification(spec);
        }

        public IEnumerable<Task> GetArchiveTasksBySearchCriteria(Account account, SearchModel modelSearch)
        {
            var specification = new WhereTaskStatusIs(TaskStatus.Closed) &
                                GetResultQueryBySearchCriteria(modelSearch)
                                &
                                (AllowedTasksByAllRoleAccountInThem(account) |
                                 AllTasksInSelfDepartments(account));
            return GetTasksBySpecification(specification).OrderByDescending(x => x.DateClose);
        }

        public IEnumerable<Task> GetNotArchiveTasksBySearchCriteria(Account account, SearchModel modelSearch)
        {
            var specification = AllWithoutClosedTasks() & GetResultQueryBySearchCriteria(modelSearch)
                                &
                                (AllowedTasksByAllRoleAccountInThem(account) |
                                 AllTasksInSelfDepartments(account));
            return GetTasksBySpecification(specification).OrderByDescending(x => x.DateCreate);
        }

        public IEnumerable<Task> UnclosedTasksWithExpireReminder(DateTime dateTime)
        {
            var specification = AllWithoutClosedTasks() & new WhereExpirationReminder(dateTime);
            return GetTasksBySpecification(specification);
        }

        public IQueryable<Task> GetTasksWithRatingsInCurrentDepartment(Department department, DateTime start, DateTime end)
        {
            return FilterBy(x => x.Department.UID == department.UID)
                .Where(x => x.Ratings.Any(z => z.DateCreate >= start && z.DateCreate <= end));
        }

        public virtual IQueryable<Task> GetArchiveTasksByDepartmentForOtherRole(Account account, Department department)
        {
            return FilterBy(x => x.Status == TaskStatus.Closed)
                .Where(x => x.Department.UID == department.UID)
                .Where(x => x.Owner.UID == account.UID || x.ParticipantsWithOwner.Any(p => p.Login.UID == account.UID)).
                OrderByDescending(x => x.DateClose);
        }

        public virtual IQueryable<Task> GetTasksByDepartmentForOtherRole(Account account,
                                                                         Department department)
        {
            return FilterBy(x => x.Department.UID == department.UID)
                .Where(
                    (new WhereTaskStatusIs(TaskStatus.InProgress) | new WhereTaskStatusIs(TaskStatus.WaitingToClose)).
                        IsSatisfiedBy())
                .Where(x => x.Owner.UID == account.UID || x.ParticipantsWithOwner.Any(p => p.Login.UID == account.UID));
        }

        public virtual IQueryable<Task> GetTasksByDepartment(Department department)
        {
            return FilterBy(x => x.Department.UID == department.UID)
                .Where(
                    (new WhereTaskStatusIs(TaskStatus.InProgress) | new WhereTaskStatusIs(TaskStatus.WaitingToClose)).
                        IsSatisfiedBy());
        }

        public virtual IEnumerable<Task> GetTasksInProgressWhereIamExecutor(Account account)
        {
            return FilterBy(
                (new WhereTaskStatusIs(TaskStatus.InProgress) | new WhereTaskStatusIs(TaskStatus.WaitingToClose))
                    .IsSatisfiedBy())
                .Where(x => x.Executor.UID == account.UID)
                .OrderBy(x => x.Department);
        }

        public virtual IEnumerable<Task> GetTasksInProgressWhereIamParticipant(Account account)
        {
            return FilterBy(new AccountIsParticipant(account))
                .Where((new WhereTaskStatusIs(TaskStatus.InProgress) | new WhereTaskStatusIs(TaskStatus.WaitingToClose))
                           .IsSatisfiedBy())
                .OrderBy(x => x.Department);
        }

        public virtual IEnumerable<Task> GetTasksInProgressWhereIamOwner(Account account)
        {
            return FilterBy(
                (new WhereTaskStatusIs(TaskStatus.InProgress) | new WhereTaskStatusIs(TaskStatus.WaitingToClose))
                    .IsSatisfiedBy())
                .Where(x => x.Owner.UID == account.UID)
                .OrderBy(x => x.Department);
        }

        #endregion

        public IEnumerable<Task> GetAllTask()
        {
            return All();
        }

        private Specification<Task> AllowedTasksByAllRoleAccountInThem(Account account)
        {
            return new AccountIsOwner(account)
                   | new AccountIsExecutor(account)
                   | new AccountIsParticipant(account);
        }

        private Specification<Task> AllTasksInSelfDepartments(Account account)
        {
            return new AccountExecutorInDepartment(account);
        }

        private Specification<Task> GetResultQueryBySearchCriteria(SearchModel searchModel)
        {
            Specification<Task> query = new EmptySpecification();
            if (!String.IsNullOrEmpty(searchModel.TaskName))
                query = query & new ContainsInTaskName(searchModel.TaskName);
            if (searchModel.TaskGuid.HasValue)
                query = query & new ContainsInTaskId(searchModel.TaskGuid.Value);
            if (searchModel.TaskIntCode.HasValue)
                query = query & new ContainsInTaskCode(searchModel.TaskIntCode.Value);
            if (!String.IsNullOrEmpty(searchModel.FileName)) query = query & new ContainsInFileName(searchModel.FileName);
            if (!String.IsNullOrEmpty(searchModel.Tag)) query = query & new ContainsInTag(searchModel.Tag);
            if (!String.IsNullOrEmpty(searchModel.DepartmentName)) query = query & new ContainsInDepartmentName(searchModel.DepartmentName);
            if (!String.IsNullOrEmpty(searchModel.Executor)) query = query & new ContainsInExecutorFullName(searchModel.Executor);
            if (!String.IsNullOrEmpty(searchModel.Owner)) query = query & new ContainsInOwnerFullName(searchModel.Owner);
            if (!String.IsNullOrEmpty(searchModel.Participant)) query = query & new ContainsInParticipantFullName(searchModel.Participant);
            if (!String.IsNullOrEmpty(searchModel.Comment)) query = query & new ContainsInComment(searchModel.Comment);
            if (!String.IsNullOrEmpty(searchModel.DateCreateFrom) && !String.IsNullOrEmpty(searchModel.DateCreateTo)){
                query = query & new DateCreatePeriod((DateTime) Tools.StringTodMyyyy(searchModel.DateCreateFrom),
                                                     (DateTime) Tools.StringTodMyyyy(searchModel.DateCreateTo));
            }
            if (!String.IsNullOrEmpty(searchModel.DateCloseFrom) && !String.IsNullOrEmpty(searchModel.DateCloseTo)){
                query = query & new DateClosePeriod((DateTime) Tools.StringTodMyyyy(searchModel.DateCloseFrom),
                                                    (DateTime) Tools.StringTodMyyyy(searchModel.DateCloseTo));
            }
            return query;
        }

        private Specification<Task> AllWithoutClosedTasks()
        {
            return new WhereTaskStatusIs(TaskStatus.WaitingToClose) | new WhereTaskStatusIs(TaskStatus.InProgress) |
                   new WhereTaskStatusIs(TaskStatus.New);
        }

        private IEnumerable<Task> GetTasksBySpecification(Specification<Task> specification)
        {
            return FilterBy(specification);
        }
    }
}